package com.cskaoyan.javase._1extends._7initialization._1basic;

import javax.lang.model.element.AnnotationMirror;

/**
 * 我们通过一个引例，来讲解子类对象的初始化问题:
 * 创建一个Person类，有name属性和eat方法
 * 创建一个Star类，有stageName属性和sing方法
 * Star类要继承Person类
 * 创建Star类对象，画出这个过程的内存图，描述子类对象初始化的过程。
 *
 * 总结/结论:
 *      1.创建子类对象,会触发父类的类加载,而且类加载的顺序是"先父后子"的
 *          思考:
 *          触发子类的类加载,会不会优先类加载父类呢?会不会触发父类的类加载?
 *          是
 *          所以这是一种新的类加载的时机,现在一共学习了四种类加载的时机:
 *              a.new对象
 *              b.启动main方法
 *              c.访问静态成员
 *              d.触发子类类加载,优先类加载父类
 *         子类触发类加载,优先类加载父类
 *         而且是从顶层父类开始,向下完成类加载
 *
 *      2.new关键字创建子类对象,只会在堆上创建一个子类对象
 *          不会创建父类对象
 *          堆上只有一个对象,就是子类对象,没有父类对象
 *
 *      3.子类对象中会专门开辟一片独立的区域，用来存储父类的成员变量，
 *      而子类自身的成员仍会存储在自身对象当中。
 *          这样子类对象就被划分为了两个区域:
 *              a.独属于存储父类成员变量的区域
 *                  子类对象中这片独属于父类成员变量的区域,
 *                      可以近似的看成是一个父类对象
 *                      这片区域使用super关键字指向它
 *                      super指向子类对象中那片独属于父类成员的区域
 *                      近似看成super指向父类对象
 *              b.存储子类自身成员变量的区域
 *                  注意,this不是指向子类自身成员变量的区域,
 *                      而是指向整个子类对象
 *
 *      4.子类对象中两片区域分别存储子类成员变量和父类成员变量,
 *          那么父子类成员变量的赋值顺序是什么样的呢?
 *          首先我们要强调一点:
 *              成员变量永远最先进行默认初始化,这是JVM在创建对象结构时,自动给出的
 *              成员变量的赋值永远是在默认值的基础上进行的,它们的先后顺序没有必要考虑,无所谓
 *          那么除了默认初始化,还有:
 *              a.显式赋值
 *              b.构造代码块赋值
 *              c.构造器赋值
 *          它们的赋值顺序是什么样的呢?
 *              猜想"先父后子"
 *              这个猜想是正确的
 *              在子类对象的初始化过程中
 *              父类的成员变量要先于子类成员变量赋值
 *              在子类成员变量只有默认值的时候,父类的成员变量已经开始赋值了,完成父类的成员变量赋值后,才能开始子类的成员变量赋值.
 *
 *    5.为什么子类对象初始化中,成员变量的赋值"先父后子"呢?
 *    思考: 如果要保证父类成员变量的所有赋值手段,都在子类成员变量的赋值手段之前执行,需要保证什么?
 *    只需要保证父类成员变量的赋值手段中,最后执行的手段,在子类成员变量的赋值手段之前执行就可以.
 *    那么父类成员变量的赋值手段中,最后执行的是谁呢?
 *    构造器
 *    于是只需要保证父类的构造器先于子类构造器执行
 *    那么父类的所有赋值手段,都会先于子类执行
 *
 *    以上都是猜想,怎么验证呢?
 *    实际Java是不是这么做的呢?
 *    构造器的执行虽然不好直接验证,但是构造代码块随着构造器而执行,只要构造代码块"先父后子"执行,那么构造器肯定也是"先父后子"执行的
 *    通过代码执行,验证以上猜想是正确的
 *
 *    Java中子类对象初始化中,成员变量的赋值"先父后子"是通过构造器的执行"先父后子"完成的!
 *
 *    6.构造器的"先父后子"是怎么完成的?
 *      在构造器中可以用this关键字表示调用自身其他构造器
 *      那么可不可以用一个关键字去调用父类构造器呢?
 *
 *      用super来表示调用父类构造器
 *      语法和this表示构造器一样:
 *          super(实参列表);
 *
 *   7.子类对象的隐式初始化
 *      在java类中,一个构造器当中,如果构造器代码的第一行没有显式使用this或者super表示调用其他构造器
 *      那么它就永远隐含一句:
 *          super();
 *      表示调用父类的无参构造器.
 *      用来完成子类对象初始化时,构造器执行的"先父后子".
 *
 *  8.子类对象的显式初始化
 *      子类对象的隐式初始化调用父类的无参构造器,无参构造不能完成父类成员变量的赋值
 *      所以需要子类对象的显式初始化来直接调用父类的有参构造,完成父类成员赋值.
 *      语法:
 *          直接在子类构造器的第一行写:
 *          super(实参列表);
 *      这种直接在子类构造器中,调用父类构造器的方式完成子类对象初始化,称之子类对象的显式初始化
 *
 *
 * @since 11:15
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        Star s = new Star();
        // System.out.println(s.stageName);

        Star s2 = new Star("李四", "长风");
        System.out.println(s2.name);
        System.out.println(s2.stageName);
    }
}

class Person {
    /*static {
        System.out.println("Person类完成类加载了!");
    }*/

    /*{
        System.out.println("Person");
    }*/

    String name = "张三";

    public void eat() {
        System.out.println(name + "要吃饭!");
    }

    public Person() {
        // super(); 表示指向父类Object类的无参构造
        /*
            Object类一定有无参构造器
         */
        System.out.println("777");
    }

    public Person(String name) {
        this.name = name;
    }
}

class Star extends Person {
   /* static {
        System.out.println("Star类完成类加载了!");
    }*/

    /*{
        System.out.println("Star");
    }*/

    String stageName = name + "的儿子";

    public void sing() {
        System.out.println("唱一首爱的供养");
    }

    public Star() {
        // super();
        System.out.println("666");
    }

    public Star(String stageName) {
        // super();
        this.stageName = stageName;
    }

    public Star(String name, String stageName) {
        // 子类对象的显式初始化
        super(name);
        this.stageName = stageName;
    }
}